/*--------- DS18B20 -----------
Function: Display temperature by UART
Board: STC8H8K64U
Main Fosc: 22.1184 MHz
Baud rate: 9600 bps
Connect: DQ --> P20
Author: Jin-Lei Li
Email: lijinlei0907@163.com
Date: April 06, 2024
Ref: https://www.stcaimcu.com/forum.php?mod=viewthread&tid=323&extra=page%3D1
Ref: UART section in STC32 series datasheet
------------------------------*/

#include "STC8H.h"
#include "intrins.h"

typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long int u32;

#define MAIN_Fosc     22118400UL
#define Baudrate      9600
#define BRT           (65536 - (MAIN_Fosc/Baudrate + 2)/4)

sbit DQ = P2^0;

/*-- UART --*/
bit busy;
u8 wptr;//write put string
u8 rptr;//receive put string
u8 buffer; // 缓冲
u8 comdata[3]={0};      //定义数组数据(8位)，存放串口命令数据
u8 recv_flag=0; // 串口收到信息标记
/*---------*/
bit MinusFlag;		//温度正负  0：正数  1：负数
//u16 Temper; // temperature
void GPIO_Init();
void UartInit();
void UartSend(u8 dat);
void UartSendStr(u8 *p);
void Display();
void Delay_ms(u16 ms);
void receive_data(void);      //接收串口数据
void test_do_data(void);      //测试串口数据是否正确，并更新数据
/*----------- Main Program ----------------*/
void main(void)
{
	P_SW2 |= 0x80; //扩展寄存器(XFR)访问使能
	
	GPIO_Init();
  UartInit();
	ES = 1;
  EA = 1;
  while(1)
  {
		if(recv_flag==1)
		{
			recv_flag=0;
			receive_data();
			test_do_data();
			//Delay_ms(500);
		}
  }
}
/*-------------- GPIO initialize ------------------*/
void GPIO_Init()
{
	P0M1 = 0x00;   P0M0 = 0x00;
  P1M1 = 0x00;   P1M0 = 0x00;
  P2M1 = 0x00;   P2M0 = 0x00;
  P3M1 = 0x00;   P3M0 = 0x00;
  P4M1 = 0x00;   P4M0 = 0x00;
  P5M1 = 0x00;   P5M0 = 0x00;
	P6M1 = 0x00;   P6M0 = 0x00;
  P7M1 = 0x00;   P7M0 = 0x00;
}
/*----------- Delay time ----------------*/
void Delay1us(void)	//@22.1184MHz
{
	u8 data i;

	_nop_();
	i = 5;
	while (--i);
}

void Delay60us(void)	//@22.1184MHz
{
	u8 data i, j;

	_nop_();
	_nop_();
	i = 2;
	j = 182;
	do
	{
		while (--j);
	} while (--i);
}

void Delay480us(void)	//@22.1184MHz
{
	u8 data i, j;

	_nop_();
	i = 14;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}

void Delay_ms(u16 ms)
{
	u16 i;
  do{
      i = MAIN_Fosc / 10000;
      while(--i);
    }while(--ms);
}
/*-------------- UART Send byte ------------------*/
void UartSend(u8 dat)
{
    //while (busy);
    //busy = 1;
    SBUF = dat;
	while(!TI); // 相当于 TI==0
		TI=0;
}
/*-------------- UART Send string ------------------*/
void UartSendStr(u8 *p)
{
    while (*p!='\0')//字符串发送检测是否发完
    {
        UartSend(*p++);
    }
}
/*-------------- UART isr ------------------*/
void UartIsr() interrupt 4
{
    if (RI)
    {
        RI = 0;
        buffer = SBUF; // 缓冲
				recv_flag=1;//增加一个标志，看是否接收完毕，在主函数中实现功能
    }
}
/*---------------- UART initial -----------------*/
void UartInit()
{
  SCON = 0x50;
	TMOD = 0x00;
	TL1 = BRT;
	TH1 = BRT>>8;
	TR1 = 1;
  AUXR = 0x40;
  wptr = 0x00;
  rptr = 0x00;
  busy = 0;
	//ET1 = 0;		//禁止定时器%d中断
	//ES=1;               //串口中断打开
	//EA=1;
}
/*----------- Initialize DS18B20 ----------------*/
void Init_DS18B20(void)//初始化ds1820
{
  bit flag = 1;
  while( flag )
	{
		DQ = 0;				//输出低电平
		Delay480us();
		DQ = 1;				//输出高电平
		Delay60us();
		flag = DQ;			//读取当前电平
		Delay480us();	
	}
}
void DS18b20_Write_0(void)		//写逻辑0码
{
	DQ = 0;				//输出低电平
	Delay60us();
	DQ = 1;				//输出高电平
	Delay1us();		
	Delay1us();	
}

void DS18b20_Write_1(void)		//写逻辑1码
{
	DQ = 0;				//输出低电平
	Delay1us();	
	Delay1us();	
	DQ = 1;				//输出高电平
	Delay60us();	
}
/*----------- Read level ----------------*/
bit DS18b20_Read(void)		//读取电平
{
	bit state ;
	
	DQ = 0;				//输出低电平
	Delay1us();	
	Delay1us();	
	DQ = 1;				//输出低电平
	Delay1us();	
	Delay1us();	
	state = DQ;			//读取当前电平

	Delay60us();

	return 	state;
}
/*----------- Write byte ----------------*/
void WriteOneChar(u8 dat)//写一个字节
{
  u8 i;
	for( i=0;i<8;i++ )		//循环八次
	{
		if( dat & 0x01 )
			DS18b20_Write_1();
		else
			DS18b20_Write_0();
		dat >>=1;
	}
}
/*----------- Read byte ----------------*/
u8 ReadOneChar(void)//读一个字节
{
    u8 i = 0;
    u8 dat = 0;
    for (i=8;i>0;i--)
    {
      DQ = 0; // 给脉冲信号
      dat >>= 1;
      DQ = 1; // 给脉冲信号
      if( DS18b20_Read() )	//如果读取到的是1，
			dat |= 0x80;
    }
    return dat;
}

/*----------- Start ----------------*/
void Start_Convert()
{
	Init_DS18B20();
	WriteOneChar(0xcc);
	WriteOneChar(0x44);
}
/*----------- Read Temperature ----------------*/
u16 Readtemp(void)
{
  u8 Low = 0;
  u8 High = 0;
  u16 Temper = 0; // temperature
  float Tt = 0;
	
	Start_Convert();
	while(!DQ);				//4.等待DQ变成高电平
	
	Init_DS18B20();
	WriteOneChar(0xCC); //跳过读序号列号的操作
	WriteOneChar(0xBE); //读取温度寄存器
	
	Low = ReadOneChar();   //连续读两个字节数据   //读低8位
	High = ReadOneChar();                          //读高8位
	
	if( High & 0XF8 )							//有1出现就是负数
	{
		MinusFlag = 1;							//标志位负数
		Temper = (High<<8) | Low;		//将温度换算成16位
		Temper = (~Temper) +1; 			//按位取反+1
		Tt = Temper*0.0625;		//得到真实十进制温度值，因为DS18B20可以精确到0.0625度，所以读回数据的最低位代表的是0.0625度
		Temper = Tt*10 + 0.5; //放大十倍，以便保留一位小数，并四舍五入
	}
	else
	{
		MinusFlag = 0;							//标志位正数
		Temper = (High<<8) | Low;		//将温度换算成16位
		Tt = Temper*0.0625;		//最终温度保留一位小数
		Temper = Tt*10 + 0.5;
	}
	return Temper;
}
/*----------- Display temperature ----------------*/
void Display()
{
    u16 val;
    u16 shi,ge,xiaoshu;
    val = Readtemp();
    shi = val/100+48;
    ge = val/10%10+48;
    xiaoshu = val%10+48;
	if( MinusFlag==1 ) // negtive
	{
		//UartSendStr("Temperature: ");
		UartSend('-');
    UartSend(shi);
    UartSend(ge);
    UartSend('.');
    UartSend(xiaoshu);
		UartSendStr("\r\n");
	}
	else // MinusFlag==0, positive
	{
		//UartSendStr("Temperature: ");
    UartSend(shi);
    UartSend(ge);
    UartSend('.');
    UartSend(xiaoshu);
		UartSendStr("\r\n");
	}
}

/*--------------------- 数据接收与测试 ----------------------------*/
void receive_data(void)       
{
  int i ;
  for(i=0;i<3;i++)
  {
    comdata[i] = buffer; // 将缓冲的数据赋值给数组
    Delay_ms(2); // 延时一会，让串口缓存准备好下一个字节，不延时可能会导致数据丢失
  }
}

void test_do_data(void) // 测试并执行命令
{
  if(comdata[0] == 0x55) //0x55和0xAA均为判断是否为有效命令
  {
    if(comdata[1] == 0xAA)
    {
      if(comdata[2] == 0xFF)
      {
        Display();
      }
    }
  }
}

